package com.old.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.old.entity.History;
import com.old.entity.Post;
import com.old.mapper.*;
import com.old.result.PageBean;
import com.old.service.PostService;
import com.old.service.VideoService;
import com.old.vo.VideoVO;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

@Service
@RequiredArgsConstructor
@Slf4j
public class Recommendationservice {
    private final HistoryMapper historyMapper;
    private final PostMapper postMapper;
    private final VideoMapper videoMapper;
    private final RecommendMapper recommendMapper;

    private final PostService postService;
    private final VideoService videoService;
    private final UserMapper userMapper;

    //推荐列表-post
    public PageBean getRecommendedPosts(Integer pageNo, Integer pageSize, Integer sortBy, String searchTerms,Integer userId) throws JsonProcessingException {
        List<Integer> recommendedIds;

        //当搜索或者空用户时使用默认分页方法
        if (searchTerms != null && !searchTerms.isEmpty() || userId == null){
            return postService.pageList(pageNo,pageSize,sortBy,searchTerms,userId);
        }

        // 尝试取出sql提前计算好的列表
        Map<String,Object> recommendData = recommendMapper.getRecommendContentIdsWithTime(userId, 1);
        // 取出推荐列表
        String contentIdsJson = (String) recommendData.get("content_ids");
        LocalDateTime updateTime = (LocalDateTime) recommendData.get("update_time");
        if (JSON.parseArray(contentIdsJson, Integer.class).isEmpty()) {
            // 如果content_ids为null，直接返回默认分页结果
            return postService.pageList(pageNo, pageSize, sortBy, searchTerms, userId);
        }
        // 30分钟内的数据就直接用
        if(updateTime!= null &&
                (updateTime.isAfter(LocalDateTime.now().minusMinutes(30)))){
            recommendedIds = JSON.parseArray(contentIdsJson, Integer.class);
        }else{
            // 大于30分钟的重新计算
            recommendedIds = getRecommendedPostList(userId);
            if (recommendedIds.isEmpty()) {
                // 当实时推荐列表为空时，使用默认分页方法
                return postService.pageList(pageNo, pageSize, sortBy, searchTerms, userId);
            }
            // 存储推荐列表到数据库
            String contentIds = JSON.toJSONString(recommendedIds);
            recommendMapper.upsertRecommendList(userId, 1, contentIds);
        }

        // 分页处理
        PageHelper.startPage(pageNo, pageSize);
        List<Post> combinedList = postMapper.selectPostsWithRecommendFlag(recommendedIds);
        PageInfo<Post> pageInfo = new PageInfo<>(combinedList);

        PageBean pageBean = new PageBean(pageInfo.getTotal(), pageInfo.getList());

        List list_init = pageBean.getList();

        for (Object o : list_init) {
            Post post = (Post) o;
            // 解析参与人数
            ObjectMapper objectMapper = new ObjectMapper();
            // 解析为JsonNode对象
            if (post.getLikeMan() != null) {
                JsonNode jsonNode = objectMapper.readTree(post.getLikeMan());
                // 获取数组元素个数
                int elementCount = jsonNode.size();
                if (userId != null){
                    // 将字符串转换为JSONArray  likeman="[47, 2]",    例如userid=4，现在是likeman中存在4，就直接true了 ，所以不能用contains方法
                    JSONArray jsonArray = JSON.parseArray(post.getLikeMan());
                    // 使用FastJSON内置方法检查元素是否存在
                    boolean isGood = jsonArray.contains(userId);
                    // 设置帖子的状态
                    post.setIsgood(isGood);
                } else {
                    post.setIsgood(false);
                }
                post.setLikeMan(Integer.toString(elementCount));
            }
        }

        return new PageBean(pageInfo.getTotal(), list_init);
    }

    //推荐列表-video
    public PageBean getRecommendedVideos(Integer pageNo, Integer pageSize, Integer sortBy, String searchTerms,Integer userId) throws JsonProcessingException {
        List<Integer> recommendedIds;

        //当搜索或者空用户时使用默认分页方法
        if (searchTerms != null && !searchTerms.isEmpty() || userId == null){
            log.info("搜索video!!");
            return videoService.pageList(pageNo,pageSize,sortBy,searchTerms,userId);
        }

        // 尝试取出sql提前计算好的列表
        Map<String,Object> recommendData = recommendMapper.getRecommendContentIdsWithTime(userId, 2);
        // 取出推荐列表
        String contentIdsJson = (String) recommendData.get("content_ids");
        LocalDateTime updateTime = (LocalDateTime) recommendData.get("update_time");
        // 判断content_ids是否为空
        if (JSON.parseArray(contentIdsJson, Integer.class).isEmpty()) {
            // 如果content_ids为null，直接返回默认分页结果
            return videoService.pageList(pageNo, pageSize, sortBy, searchTerms, userId);
        }
        // 30分钟内的数据就直接用
        if(updateTime!= null &&
                (updateTime.isAfter(LocalDateTime.now().minusMinutes(30)))){
            recommendedIds = JSON.parseArray(contentIdsJson, Integer.class);
        }else{
            // 大于30分钟的重新计算
            recommendedIds = getRecommendedVideoList(userId);
            if (recommendedIds.isEmpty()) {
                // 当实时推荐列表为空时，使用默认分页方法
                return videoService.pageList(pageNo, pageSize, sortBy, searchTerms, userId);
            }
            // 存储推荐列表到数据库
            String contentIds = JSON.toJSONString(recommendedIds);
            recommendMapper.upsertRecommendList(userId, 2, contentIds);
        }


        // 分页处理
        PageHelper.startPage(pageNo, pageSize);
        List<VideoVO> combinedList = videoMapper.selectVideosWithRecommendFlag(recommendedIds);
        PageInfo<VideoVO> pageInfo = new PageInfo<>(combinedList);

        PageBean pageBean = new PageBean(pageInfo.getTotal(), pageInfo.getList());
        List list_init = pageBean.getList();

        for (Object object : list_init) {
            VideoVO videoVO = (VideoVO) object;

            // 解析参与人数
            ObjectMapper objectMapper = new ObjectMapper();
            // 解析为JsonNode对象
            if (videoVO.getLikeMan() != null) {
                JsonNode jsonNode = objectMapper.readTree(videoVO.getLikeMan());
                // 获取数组元素个数
                int elementCount = jsonNode.size();
                if (userId != null){
                    // 将字符串转换为JSONArray
                    JSONArray jsonArray = JSON.parseArray(videoVO.getLikeMan());
                    // 使用FastJSON内置方法检查元素是否存在
                    boolean isGood = jsonArray.contains(userId);
                    // 设置帖子的状态
                    videoVO.setIsgood(isGood);
                } else {
                    videoVO.setIsgood(false);
                }
                videoVO.setLikeMan(Integer.toString(elementCount));
            }
        }


        return new PageBean(pageInfo.getTotal(), list_init);
    }

    // 算法抽离-post
    private List<Integer> getRecommendedPostList(Integer userId) {
        // 1. 获取所有用户的浏览记录
        List<History> allHistory = historyMapper.selectListPost(null);
        // 2. 构建用户-帖子浏览关系映射
        Map<Integer, Set<Integer>> userPostMap = new HashMap<>();
        for (History history : allHistory) {
            userPostMap.computeIfAbsent(history.getUserId(), k -> new HashSet<>())
                    .add(history.getPostId());
        }
        // 3. 获取当前用户浏览过的帖子
        Set<Integer> currentUserPosts = userPostMap.getOrDefault(userId, Collections.emptySet());
        // 4. 计算帖子相似度（基于物品的协同过滤）
        Map<Integer, Integer> postSimilarity = new HashMap<>();
        if (!currentUserPosts.isEmpty()) {
            for (Map.Entry<Integer, Set<Integer>> entry : userPostMap.entrySet()) {
                // 排除当前用户
                if (entry.getKey().equals(userId)) continue;

                Set<Integer> otherUserPosts = entry.getValue();
                // 计算交集
                Set<Integer> intersection = new HashSet<>(currentUserPosts);
                intersection.retainAll(otherUserPosts);

                // 如果有共同浏览的帖子，则计算相似度
                if (!intersection.isEmpty()) {
                    for (Integer postId : otherUserPosts) {
                        if (!currentUserPosts.contains(postId)) {
                            postSimilarity.put(postId,
                                    postSimilarity.getOrDefault(postId, 0) + intersection.size());
                        }
                    }
                }
            }
        } else {
            log.info("用户没有浏览历史,返回新列表");
            return new ArrayList<>();
        }
        // 5. 排序并过滤已浏览帖子
        List<Integer> recommendedIds = postSimilarity.entrySet().stream()
                .sorted(Map.Entry.<Integer, Integer>comparingByValue().reversed())
                .map(Map.Entry::getKey)
                .filter(id -> !currentUserPosts.contains(id))
                .collect(Collectors.toList());
        log.info(" 推荐列表为: {}",recommendedIds);
        return recommendedIds;
    }
    // 算法抽离-video
    private List<Integer> getRecommendedVideoList(Integer userId) {
        // 1. 获取所有用户的浏览记录
        List<History> allHistory = historyMapper.selectListVideo(null);
        // 2. 构建用户-视频浏览关系映射
        Map<Integer, Set<Integer>> userVideoMap = new HashMap<>();
        for (History history : allHistory) {
            userVideoMap.computeIfAbsent(history.getUserId(), k -> new HashSet<>())
                    .add(history.getVideoId());
        }
        // 3. 获取当前用户浏览过的视频
        Set<Integer> currentUserVideos = userVideoMap.getOrDefault(userId, Collections.emptySet());
        // 4. 计算视频相似度（基于物品的协同过滤）
        Map<Integer, Integer> videoSimilarity = new HashMap<>();
        if (!currentUserVideos.isEmpty()) {
            for (Map.Entry<Integer, Set<Integer>> entry : userVideoMap.entrySet()) {
                // 排除当前用户
                if (entry.getKey().equals(userId)) continue;

                Set<Integer> otherUserVideos = entry.getValue();
                // 计算交集
                Set<Integer> intersection = new HashSet<>(currentUserVideos);
                intersection.retainAll(otherUserVideos);

                // 如果有共同浏览的视频，则计算相似度
                if (!intersection.isEmpty()) {
                    for (Integer videoId : otherUserVideos) {
                        if (!currentUserVideos.contains(videoId)) {
                            videoSimilarity.put(videoId,
                                    videoSimilarity.getOrDefault(videoId, 0) + intersection.size());
                        }
                    }
                }
            }
        } else {
            // 当前用户没有浏览过任何视频 返回空值
            log.info("用户没有浏览列表");
            return new ArrayList<>();
        }
        // 5. 排序并过滤已浏览视频
        List<Integer> recommendedIds = videoSimilarity.entrySet().stream()
                .sorted(Map.Entry.<Integer, Integer>comparingByValue().reversed())
                .map(Map.Entry::getKey)
                .filter(id -> !currentUserVideos.contains(id))
                .collect(Collectors.toList());

        log.info(" 计算推荐列表为: {}",recommendedIds);
        return recommendedIds;
    }

    // 定时生成推荐列表
    // 每小时20分执行一次全量更新列表构建
    @Scheduled(cron = "0 20 * * * ?")
    private void batchGenerateRecommendations() {
        List<Integer> userIds = userMapper.getUserIds();
        // 错误收集列表
        List<Integer> failedUserIds = new ArrayList<>();

        userIds.forEach(userId -> {
            try {
                // 生成帖子推荐
                List<Integer> postRecommendations = getRecommendedPostList(userId);
                recommendMapper.upsertRecommendList(userId, 1, JSON.toJSONString(postRecommendations));

                // 生成视频推荐
                List<Integer> videoRecommendations = getRecommendedVideoList(userId);
                recommendMapper.upsertRecommendList(userId, 2, JSON.toJSONString(videoRecommendations));

            } catch (Exception e) {
                log.error("生成用户{}推荐列表失败", userId, e);
                // 记录失败用户ID
                failedUserIds.add(userId);
            }
        });

        // 错误汇总日志
        if (!failedUserIds.isEmpty()) {
            log.error("推荐列表生成失败用户列表(共{}个): {}",
                    failedUserIds.size(),
                    failedUserIds.stream()
                            .map(Object::toString)
                            .collect(Collectors.joining(", ")));
        }

        log.info("生成推荐列表完成,共处理{}个用户,其中成功{}个,失败{}个",
                userIds.size(),
                userIds.size() - failedUserIds.size(),
                failedUserIds.size());
    }
}

